//===----------------------------------------------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// simd.h overlays for Swift
//===----------------------------------------------------------------------===//

import Swift
import Darwin
@_exported import simd

%{
component = ['x','y','z','w']
scalar_types = ['Float','Double','Int32','UInt32']
ctype = { 'Float':'float', 'Double':'double', 'Int32':'int', 'UInt32':'uint'}
llvm_type = { 'Float':'FPIEEE32', 'Double':'FPIEEE64', 'Int32':'Int32', 'UInt32':'Int32' }
floating_types = ['Float','Double']
cardinal = { 2:'two', 3:'three', 4:'four'}
}%

%for scalar in scalar_types:
% for count in [2, 3, 4]:
%  vectype = ctype[scalar] + str(count)
%  llvm_vectype = "Vec" + str(count) + "x" + llvm_type[scalar]
%  vecsize = (8 if scalar == 'Double' else 4) * (4 if count == 3 else count)
%  extractelement = "extractelement_" + llvm_vectype + "_Int32"
%  insertelement = "insertelement_" + llvm_vectype + "_" + llvm_type[scalar] + "_Int32"
%  is_floating = scalar in floating_types
%  is_signed = scalar[0] != 'U'
%  wrap = "" if is_floating else "&"


/// A vector of ${cardinal[count]} `${scalar}`.  This corresponds to the C and
/// Obj-C type `vector_${vectype}` and the C++ type `simd::${vectype}`.
@_fixed_layout
@_alignment(${vecsize})
public struct ${vectype} {

  public var _value: Builtin.${llvm_vectype}

% for i in xrange(count):
  public var ${component[i]} : ${scalar} {
    @_transparent
    get {
      let elt = Builtin.${extractelement}(_value,
        (${i} as Int32)._value)

      return ${scalar}(elt)
    }
    @_transparent
    set {
      _value = Builtin.${insertelement}(_value,
        newValue._value,
        (${i} as Int32)._value)
    }
  }
% end

  /// Initialize to the zero vector.
  @_transparent
  public init() { self.init(0) }

  @_transparent
  public init(_ _value: Builtin.${llvm_vectype}) {
    self._value = _value
  }

  /// Initialize a vector with the specified elements.
  @_transparent
  public init(${', '.join(['_ ' + c + ': ' + scalar for c in component[:count]])}) {
    var v: Builtin.${llvm_vectype} = Builtin.zeroInitializer()
% for i in xrange(count):
    v = Builtin.${insertelement}(v,
      ${component[i]}._value,
      (${i} as Int32)._value)
% end
    _value = v
  }

  /// Initialize a vector with the specified elements.
  @_transparent
  public init(${', '.join([c + ': ' + scalar for c in component[:count]])}) {
    self.init(${', '.join(component[:count])})
  }

  /// Initialize to a vector with all elements equal to `scalar`.
  @_transparent
  public init(_ scalar: ${scalar}) {
    self.init(${', '.join(['scalar']*count)})
  }

  /// Initialize to a vector with elements taken from `array`.
  ///
  /// - Precondition: `array` must have exactly ${cardinal[count]} elements.
  public init(_ array: [${scalar}]) {
  _precondition(array.count == ${count},
                "${vectype} requires a ${cardinal[count]}-element array")
    self.init(${', '.join(map(lambda i:
              'array[' + str(i) + ']',
              range(count)))})
  }

  /// Access individual elements of the vector via subscript.
  public subscript(index: Int) -> ${scalar} {
    @_transparent
    get {
      _precondition(index >= 0, "Vector index out of range")
      _precondition(index < ${count}, "Vector index out of range")
      let elt = Builtin.${extractelement}(_value,
        Int32(index)._value)
      return ${scalar}(elt)
    }
    @_transparent
    set(value) {
      _precondition(index >= 0, "Vector index out of range")
      _precondition(index < ${count}, "Vector index out of range")
      _value = Builtin.${insertelement}(_value,
        value._value,
        Int32(index)._value)
    }
  }
}

extension ${vectype} : Equatable {
  /// True iff every element of lhs is equal to the corresponding element of
  /// rhs.
  @_transparent
  public static func ==(_ lhs: ${vectype}, _ rhs: ${vectype}) -> Bool {
    return simd_equal(lhs, rhs)
  }
}

extension ${vectype} : CustomDebugStringConvertible {
  /// Debug string representation
  public var debugDescription: String {
    return "${vectype}(${', '.join(map(lambda c:
                       '\\(self['+ str(c) + '])',
                       xrange(count)))})"
  }

% if count <= 4:
  /// Helper for matrix debug representations
  internal var _descriptionAsArray: String {
    get {
      return "[${', '.join(map(lambda c:
                 '\\(self['+ str(c) + '])',
                 xrange(count)))})]"
    }
  }
% end
}

extension ${vectype} : ExpressibleByArrayLiteral {
  /// Initialize using `arrayLiteral`.
  ///
  /// - Precondition: the array literal must exactly ${cardinal[count]}
  ///   elements.
  public init(arrayLiteral elements: ${scalar}...) { self.init(elements) }
}

extension ${vectype} : Collection {
  @_transparent
  public var startIndex: Int { return 0 }

  @_transparent
  public var endIndex: Int { return ${count} }

  @_transparent
  public func index(after i: Int) -> Int { return i + 1 }
}

extension ${vectype} {
  % wrap = "" if is_floating else "&"
  % prefix = "f" if is_floating else ""
  % divide = ("f" if is_floating else ("s" if is_signed else "u")) + "div"
  /// Vector (elementwise) sum of `lhs` and `rhs`.
  @_transparent
  public static func ${wrap}+(lhs: ${vectype}, rhs: ${vectype}) -> ${vectype} {
    return ${vectype}(Builtin.${prefix}add_${llvm_vectype}(lhs._value, rhs._value))
  }

  /// Vector (elementwise) difference of `lhs` and `rhs`.
  @_transparent
  public static func ${wrap}-(lhs: ${vectype}, rhs: ${vectype}) -> ${vectype} {
    return ${vectype}(Builtin.${prefix}sub_${llvm_vectype}(lhs._value, rhs._value))
  }

  /// Negation of `rhs`.
  @_transparent
  public static prefix func -(rhs: ${vectype}) -> ${vectype} {
    return ${vectype}() ${wrap}- rhs
  }

  /// Elementwise product of `lhs` and `rhs` (A.k.a. the Hadamard or Schur
  /// vector product).
  @_transparent
  public static func ${wrap}*(lhs: ${vectype}, rhs: ${vectype}) -> ${vectype} {
    return ${vectype}(Builtin.${prefix}mul_${llvm_vectype}(lhs._value, rhs._value))
  }

  /// Scalar-Vector product.
  @_transparent
  public static func ${wrap}*(lhs: ${scalar}, rhs: ${vectype}) -> ${vectype} {
    return ${vectype}(lhs) ${wrap}* rhs
  }

  /// Scalar-Vector product.
  @_transparent
  public static func ${wrap}*(lhs: ${vectype}, rhs: ${scalar}) -> ${vectype} {
    return lhs ${wrap}* ${vectype}(rhs)
  }

  /// Elementwise quotient of `lhs` and `rhs`.
  @_transparent
  public static func /(lhs: ${vectype}, rhs: ${vectype}) -> ${vectype} {
    return ${vectype}(Builtin.${divide}_${llvm_vectype}(lhs._value, rhs._value))
  }

  /// Divide vector by scalar.
  @_transparent
  public static func /(lhs: ${vectype}, rhs: ${scalar}) -> ${vectype} {
    return lhs / ${vectype}(rhs)
  }

  /// Add `rhs` to `lhs`.
  %  if is_floating:
  @_transparent
  public static func +=(lhs: inout ${vectype}, rhs: ${vectype}) -> Void {
    lhs = lhs + rhs
  }

  /// Subtract `rhs` from `lhs`.
  @_transparent
  public static func -=(lhs: inout ${vectype}, rhs: ${vectype}) -> Void {
    lhs = lhs - rhs
  }

  /// Multiply `lhs` by `rhs` (elementwise).
  @_transparent
  public static func *=(lhs: inout ${vectype}, rhs: ${vectype}) -> Void {
    lhs = lhs * rhs
  }
  %  end

  /// Divide `lhs` by `rhs` (elementwise).
  @_transparent
  public static func /=(lhs: inout ${vectype}, rhs: ${vectype}) -> Void {
    lhs = lhs / rhs
  }

%  if is_floating:
  /// Scales `lhs` by `rhs`.
  @_transparent
  public static func *=(lhs: inout ${vectype}, rhs: ${scalar}) -> Void {
    lhs = lhs * rhs
  }

  /// Scales `lhs` by `1/rhs`.
  @_transparent
  public static func /=(lhs: inout ${vectype}, rhs: ${scalar}) -> Void {
    lhs = lhs / rhs
  }
%  else:
  // Integer vector types only support wrapping arithmetic. Make the non-
  // wrapping operators unavailable so that fixits guide users to the
  // unchecked operations.

  @available(*, unavailable, renamed: "&+",
             message: "integer vector types do not support checked arithmetic; use the wrapping operations instead")
  public static func +(x: ${vectype}, y: ${vectype}) -> ${vectype} {
    fatalError("Unavailable function cannot be called")
  }

  @available(*, unavailable, renamed: "&-",
             message: "integer vector types do not support checked arithmetic; use the wrapping operations instead")
  public static func -(x: ${vectype}, y: ${vectype}) -> ${vectype} {
    fatalError("Unavailable function cannot be called")
  }

  @available(*, unavailable, renamed: "&*",
             message: "integer vector types do not support checked arithmetic; use the wrapping operations instead")
  public static func *(x: ${vectype}, y: ${vectype}) -> ${vectype} {
    fatalError("Unavailable function cannot be called")
  }

  @available(*, unavailable, renamed: "&*",
             message: "integer vector types do not support checked arithmetic; use the wrapping operations instead")
  public static func *(x: ${vectype}, y: ${scalar}) -> ${vectype} {
    fatalError("Unavailable function cannot be called")
  }

  @available(*, unavailable, renamed: "&*",
             message: "integer vector types do not support checked arithmetic; use the wrapping operations instead")
  public static func *(x: ${scalar}, y: ${vectype}) -> ${vectype} {
    fatalError("Unavailable function cannot be called")
  }

  @available(*, unavailable,
             message: "integer vector types do not support checked arithmetic; use the wrapping operation 'x = x &+ y' instead")
  public static func +=(x: inout ${vectype}, y: ${vectype}) {
    fatalError("Unavailable function cannot be called")
  }

  @available(*, unavailable,
             message: "integer vector types do not support checked arithmetic; use the wrapping operation 'x = x &- y' instead")
  public static func -=(x: inout ${vectype}, y: ${vectype}) {
    fatalError("Unavailable function cannot be called")
  }

  @available(*, unavailable,
             message: "integer vector types do not support checked arithmetic; use the wrapping operation 'x = x &* y' instead")
  public static func *=(x: inout ${vectype}, y: ${vectype}) {
    fatalError("Unavailable function cannot be called")
  }

  @available(*, unavailable,
             message: "integer vector types do not support checked arithmetic; use the wrapping operation 'x = x &* y' instead")
  public static func *=(x: inout ${vectype}, y: ${scalar}) {
    fatalError("Unavailable function cannot be called")
  }
%  end
}

%  if is_signed:
/// Elementwise absolute value of a vector.  The result is a vector of the same
/// length with all elements positive.
@_transparent
public func abs(_ x: ${vectype}) -> ${vectype} {
  return simd_abs(x)
}
%  end

/// Elementwise minimum of two vectors.  Each component of the result is the
/// smaller of the corresponding component of the inputs.
@_transparent
public func min(_ x: ${vectype}, _ y: ${vectype}) -> ${vectype} {
  return simd_min(x, y)
}

/// Elementwise maximum of two vectors.  Each component of the result is the
/// larger of the corresponding component of the inputs.
@_transparent
public func max(_ x: ${vectype}, _ y: ${vectype}) -> ${vectype} {
  return simd_max(x, y)
}

/// Vector-scalar minimum.  Each component of the result is the minimum of the
/// corresponding element of the input vector and the scalar.
@_transparent
public func min(_ vector: ${vectype}, _ scalar: ${scalar}) -> ${vectype} {
  return min(vector, ${vectype}(scalar))
}

/// Vector-scalar maximum.  Each component of the result is the maximum of the
/// corresponding element of the input vector and the scalar.
@_transparent
public func max(_ vector: ${vectype}, _ scalar: ${scalar}) -> ${vectype} {
  return max(vector, ${vectype}(scalar))
}

/// Each component of the result is the corresponding element of `x` clamped to
/// the range formed by the corresponding elements of `min` and `max`.  Any
/// lanes of `x` that contain NaN will end up with the `min` value.
@_transparent
public func clamp(_ x: ${vectype}, min: ${vectype}, max: ${vectype})
  -> ${vectype} {
  return simd.min(simd.max(x, min), max)
}

/// Clamp each element of `x` to the range [`min`, max].  If any lane of `x` is
/// NaN, the corresponding lane of the result is `min`.
@_transparent
public func clamp(_ x: ${vectype}, min: ${scalar}, max: ${scalar})
  -> ${vectype} {
  return simd.min(simd.max(x, min), max)
}

/// Sum of the elements of the vector.
@_transparent
public func reduce_add(_ x: ${vectype}) -> ${scalar} {
  return simd_reduce_add(x)
}

/// Minimum element of the vector.
@_transparent
public func reduce_min(_ x: ${vectype}) -> ${scalar} {
  return simd_reduce_min(x)
}

/// Maximum element of the vector.
@_transparent
public func reduce_max(_ x: ${vectype}) -> ${scalar} {
  return simd_reduce_max(x)
}

%  if is_floating:

/// Sign of a vector.  Each lane contains -1 if the corresponding lane of `x`
/// is less than zero, +1 if the corresponding lane of `x` is greater than
/// zero, and 0 otherwise.
@_transparent
public func sign(_ x: ${vectype}) -> ${vectype} {
  return simd_sign(x)
}

/// Linear interpolation between `x` (at `t=0`) and `y` (at `t=1`).  May be
/// used with `t` outside of [0, 1] as well.
@_transparent
public func mix(_ x: ${vectype}, _ y: ${vectype}, t: ${vectype}) -> ${vectype} {
  return x + t*(y-x)
}

/// Linear interpolation between `x` (at `t=0`) and `y` (at `t=1`).  May be
/// used with `t` outside of [0, 1] as well.
@_transparent
public func mix(_ x: ${vectype}, _ y: ${vectype}, t: ${scalar}) -> ${vectype} {
  return x + t*(y-x)
}

/// Elementwise reciprocal.
@_transparent
public func recip(_ x: ${vectype}) -> ${vectype} {
  return simd_recip(x)
}

/// Elementwise reciprocal square root.
@_transparent
public func rsqrt(_ x: ${vectype}) -> ${vectype} {
  return simd_rsqrt(x)
}

/// Alternate name for minimum of two floating-point vectors.
@_transparent
public func fmin(_ x: ${vectype}, _ y: ${vectype}) -> ${vectype} {
  return min(x, y)
}

/// Alternate name for maximum of two floating-point vectors.
@_transparent
public func fmax(_ x: ${vectype}, _ y: ${vectype}) -> ${vectype} {
  return max(x, y)
}

/// Each element of the result is the smallest integral value greater than or
/// equal to the corresponding element of the input.
@_transparent
public func ceil(_ x: ${vectype}) -> ${vectype} {
  return __tg_ceil(x)
}

/// Each element of the result is the largest integral value less than or equal
/// to the corresponding element of the input.
@_transparent
public func floor(_ x: ${vectype}) -> ${vectype} {
  return __tg_floor(x)
}

/// Each element of the result is the closest integral value with magnitude
/// less than or equal to that of the corresponding element of the input.
@_transparent
public func trunc(_ x: ${vectype}) -> ${vectype} {
  return __tg_trunc(x)
}

/// `x - floor(x)`, clamped to lie in the range [0,1).  Without this clamp step,
/// the result would be 1.0 when `x` is a very small negative number, which may
/// result in out-of-bounds table accesses in common usage.
@_transparent
public func fract(_ x: ${vectype}) -> ${vectype} {
  return simd_fract(x)
}

/// 0.0 if `x < edge`, and 1.0 otherwise.
@_transparent
public func step(_ x: ${vectype}, edge: ${vectype}) -> ${vectype} {
  return simd_step(edge, x)
}

/// 0.0 if `x < edge0`, 1.0 if `x > edge1`, and cubic interpolation between
/// 0 and 1 in the interval [edge0, edge1].
@_transparent
public func smoothstep(_ x: ${vectype}, edge0: ${vectype}, edge1: ${vectype})
  -> ${vectype} {
  return simd_smoothstep(edge0, edge1, x)
}

/// Dot product of `x` and `y`.
@_transparent
public func dot(_ x: ${vectype}, _ y: ${vectype}) -> ${scalar} {
  return reduce_add(x * y)
}

/// Projection of `x` onto `y`.
@_transparent
public func project(_ x: ${vectype}, _ y: ${vectype}) -> ${vectype} {
  return simd_project(x, y)
}

/// Length of `x`, squared.  This is more efficient to compute than the length,
/// so you should use it if you only need to compare lengths to each other.
/// I.e. instead of writing:
///
///   if (length(x) < length(y)) { ... }
///
/// use:
///
///   if (length_squared(x) < length_squared(y)) { ... }
///
/// Doing it this way avoids one or two square roots, which is a fairly costly
/// operation.
@_transparent
public func length_squared(_ x: ${vectype}) -> ${scalar} {
  return dot(x, x)
}

/// Length (two-norm or "Euclidean norm") of `x`.
@_transparent
public func length(_ x: ${vectype}) -> ${scalar} {
  return sqrt(length_squared(x))
}

/// The one-norm (or "taxicab norm") of `x`.
@_transparent
public func norm_one(_ x: ${vectype}) -> ${scalar} {
  return reduce_add(abs(x))
}

/// The infinity-norm (or "sup norm") of `x`.
@_transparent
public func norm_inf(_ x: ${vectype}) -> ${scalar} {
  return reduce_max(abs(x))
}

/// Distance between `x` and `y`, squared.
@_transparent
public func distance_squared(_ x: ${vectype}, _ y: ${vectype}) -> ${scalar} {
  return length_squared(x - y)
}

/// Distance between `x` and `y`.
@_transparent
public func distance(_ x: ${vectype}, _ y: ${vectype}) -> ${scalar} {
  return length(x - y)
}

/// Unit vector pointing in the same direction as `x`.
@_transparent
public func normalize(_ x: ${vectype}) -> ${vectype} {
  return simd_normalize(x)
}

/// `x` reflected through the hyperplane with unit normal vector `n`, passing
/// through the origin.  E.g. if `x` is [1,2,3] and `n` is [0,0,1], the result
/// is [1,2,-3].
@_transparent
public func reflect(_ x: ${vectype}, n: ${vectype}) -> ${vectype} {
  return simd_reflect(x, n)
}

/// The refraction direction given unit incident vector `x`, unit surface
/// normal `n`, and index of refraction `eta`.  If the angle between the
/// incident vector and the surface is so small that total internal reflection
/// occurs, zero is returned.
@_transparent
public func refract(_ x: ${vectype}, n: ${vectype}, eta: ${scalar})
  -> ${vectype} {
  return simd_refract(x, n, eta)
}

%  end # if is_floating
% end # for count in [2, 3, 4]
% if is_floating:
//  Scalar versions of common operations:

/// Returns -1 if `x < 0`, +1 if `x > 0`, and 0 otherwise (`sign(NaN)` is 0).
@_transparent
public func sign(_ x: ${scalar}) -> ${scalar} {
  return simd_sign(x)
}

/// Reciprocal.
@_transparent
public func recip(_ x: ${scalar}) -> ${scalar} {
  return simd_recip(x)
}

/// Reciprocal square root.
@_transparent
public func rsqrt(_ x: ${scalar}) -> ${scalar} {
  return simd_rsqrt(x)
}

/// Returns 0.0 if `x < edge`, and 1.0 otherwise.
@_transparent
public func step(_ x: ${scalar}, edge: ${scalar}) -> ${scalar} {
  return simd_step(edge, x)
}

/// Interprets two two-dimensional vectors as three-dimensional vectors in the
/// xy-plane and computes their cross product, which lies along the z-axis.
@_transparent
public func cross(_ x: ${ctype[scalar]}2, _ y: ${ctype[scalar]}2)
  -> ${ctype[scalar]}3 {
  return simd_cross(x,y)
}

/// Cross-product of two three-dimensional vectors.  The resulting vector is
/// perpendicular to the plane determined by `x` and `y`, with length equal to
/// the oriented area of the parallelogram they determine.
@_transparent
public func cross(_ x: ${ctype[scalar]}3, _ y: ${ctype[scalar]}3)
  -> ${ctype[scalar]}3 {
  return simd_cross(x,y)
}

% end # is_floating
%end # for scalar in scalar_types

%for type in floating_types:
% for rows in [2,3,4]:
%  for cols in [2,3,4]:
%   mattype = 'simd_' + ctype[type] + str(cols) + 'x' + str(rows)
%   diagsize = rows if rows < cols else cols
%   coltype = ctype[type] + str(rows)
%   rowtype = ctype[type] + str(cols)
%   diagtype = ctype[type] + str(diagsize)
%   transtype = ctype[type] + str(rows) + 'x' + str(cols)

public typealias ${ctype[type]}${cols}x${rows} = ${mattype}

extension ${mattype} {

  /// Initialize matrix to have `scalar` on main diagonal, zeros elsewhere.
  public init(_ scalar: ${type}) {
    self.init(diagonal: ${diagtype}(scalar))
  }

  /// Initialize matrix to have specified `diagonal`, and zeros elsewhere.
  public init(diagonal: ${diagtype}) {
    self.init()
%   for i in range(diagsize):
    self.columns.${i}.${component[i]} = diagonal.${component[i]}
%   end
  }

  /// Initialize matrix to have specified `columns`.
  public init(_ columns: [${coltype}]) {
    _precondition(columns.count == ${cols}, "Requires array of ${cols} vectors")
    self.init()
%   for i in range(cols):
    self.columns.${i} = columns[${i}]
%   end
  }

  /// Initialize matrix to have specified `rows`.
  public init(rows: [${rowtype}]) {
    _precondition(rows.count == ${rows}, "Requires array of ${rows} vectors")
    self = ${transtype}(rows).transpose
  }

  /// Initialize matrix to have specified `columns`.
  public init(${', '.join(['_ col' + str(i) + ': ' + coltype
                          for i in range(cols)])}) {
    self.init()
%   for i in range(cols):
      self.columns.${i} = col${i}
%   end
  }

  /// Initialize matrix from corresponding C matrix type.
  @available(swift, deprecated: 4, message: "This conversion is no longer necessary; use `cmatrix` directly.")
  @_transparent
  public init(_ cmatrix: ${mattype}) {
    self = cmatrix
  }

  /// Get the matrix as the corresponding C matrix type.
  @available(swift, deprecated: 4, message: "This property is no longer needed; use the matrix itself.")
  @_transparent
  public var cmatrix: ${mattype} {
    return self
  }

  /// Access to individual columns.
  public subscript(column: Int) -> ${coltype} {
    get {
      switch(column) {
%   for i in range(cols):
      case ${i}: return columns.${i}
%   end
      default: _preconditionFailure("Column index out of range")
      }
    }
    set (value) {
      switch(column) {
%   for i in range(cols):
      case ${i}: columns.${i} = value
%   end
      default: _preconditionFailure("Column index out of range")
      }
    }
  }

  /// Access to individual elements.
  public subscript(column: Int, row: Int) -> ${type} {
    get { return self[column][row] }
    set (value) { self[column][row] = value }
  }
}

extension ${mattype} : CustomDebugStringConvertible {
  public var debugDescription: String {
    return "${mattype}([${', '.join(map(lambda i: \
                        '\(columns.' + str(i) + '._descriptionAsArray)',
                        range(cols)))}])"
  }
}

extension ${mattype} : Equatable {
  @_transparent
  public static func ==(lhs: ${mattype}, rhs: ${mattype}) -> Bool {
    return simd_equal(lhs, rhs)
  }
}

extension ${mattype} {

  /// Transpose of the matrix.
  @_transparent
  public var transpose: ${transtype} {
    return simd_transpose(self)
  }

%   if rows == cols:
  /// Inverse of the matrix if it exists, otherwise the contents of the
  /// resulting matrix are undefined.
  @available(macOS 10.10, iOS 8.0, tvOS 10.0, watchOS 3.0, *)
  @_transparent
  public var inverse: ${mattype} {
    return simd_inverse(self)
  }

  /// Determinant of the matrix.
  @_transparent
  public var determinant: ${type} {
    return simd_determinant(self)
  }
%   end

  /// Sum of two matrices.
  @_transparent
  public static func +(lhs: ${mattype}, rhs: ${mattype}) -> ${mattype} {
    return simd_add(lhs, rhs)
  }

  /// Negation of a matrix.
  @_transparent
  public static prefix func -(rhs: ${mattype}) -> ${mattype} {
    return ${mattype}() - rhs
  }

  /// Difference of two matrices.
  @_transparent
  public static func -(lhs: ${mattype}, rhs: ${mattype}) -> ${mattype} {
    return simd_sub(lhs, rhs)
  }

  @_transparent
  public static func +=(lhs: inout ${mattype}, rhs: ${mattype}) -> Void {
    lhs = lhs + rhs
  }

  @_transparent
  public static func -=(lhs: inout ${mattype}, rhs: ${mattype}) -> Void {
    lhs = lhs - rhs
  }

  /// Scalar-Matrix multiplication.
  @_transparent
  public static func *(lhs: ${type}, rhs: ${mattype}) -> ${mattype} {
    return simd_mul(lhs, rhs)
  }

  /// Matrix-Scalar multiplication.
  @_transparent
  public static func *(lhs: ${mattype}, rhs: ${type}) -> ${mattype} {
    return rhs*lhs
  }

  @_transparent
  public static func *=(lhs: inout ${mattype}, rhs: ${type}) -> Void {
    lhs = lhs*rhs
  }

  /// Matrix-Vector multiplication.  Keep in mind that matrix types are named
  /// `${type}NxM` where `N` is the number of *columns* and `M` is the number of
  /// *rows*, so we multiply a `${type}3x2 * ${type}3` to get a `${type}2`, for
  /// example.
  @_transparent
  public static func *(lhs: ${mattype}, rhs: ${rowtype}) -> ${coltype} {
    return simd_mul(lhs, rhs)
  }

  /// Vector-Matrix multiplication.
  @_transparent
  public static func *(lhs: ${coltype}, rhs: ${mattype}) -> ${rowtype} {
    return simd_mul(lhs, rhs)
  }

%   for k in [2,3,4]:
  /// Matrix multiplication (the "usual" matrix product, not the elementwise
  /// product).
%    restype = ctype[type] + str(k) + 'x' + str(rows)
%    rhstype = ctype[type] + str(k) + 'x' + str(cols)
  @_transparent
  public static func *(lhs: ${mattype}, rhs: ${rhstype}) -> ${restype} {
    return simd_mul(lhs, rhs)
  }

%   end # for k in [2,3,4]

%   rhstype = ctype[type] + str(cols) + 'x' + str(cols)
  /// Matrix multiplication (the "usual" matrix product, not the elementwise
  /// product).
  @_transparent
  public static func *=(lhs: inout ${mattype}, rhs: ${rhstype}) -> Void {
    lhs = lhs*rhs
  }
}

// Make old-style C free functions with the `matrix_` prefix available but
// deprecated in Swift 4.

%   if rows == cols:
@available(swift, deprecated: 4, renamed: "${mattype}(diagonal:)")
public func matrix_from_diagonal(_ d: ${diagtype}) -> ${mattype} {
  return ${mattype}(diagonal: d)
}

@available(swift, deprecated: 4, message: "Use the .inverse property instead.")
@available(macOS 10.10, iOS 8.0, tvOS 10.0, watchOS 3.0, *)
public func matrix_invert(_ x: ${mattype}) -> ${mattype} {
  return x.inverse
}

@available(swift, deprecated: 4, message: "Use the .determinant property instead.")
public func matrix_determinant(_ x: ${mattype}) -> ${type} {
  return x.determinant
}

%   end # rows == cols
@available(swift, deprecated: 4, renamed: "${mattype}")
public func matrix_from_columns(${', '.join(['_ col' + str(i) + ': ' + coltype
                                for i in range(cols)])}) -> ${mattype} {
  return ${mattype}(${', '.join(['col' + str(i) for i in range(cols)])})
}

public func matrix_from_rows(${', '.join(['_ row' + str(i) + ': ' + rowtype
                             for i in range(rows)])}) -> ${mattype} {
  return ${transtype}(${', '.join(['row' + str(i) for i in range(rows)])}).transpose
}

@available(swift, deprecated: 4, message: "Use the .transpose property instead.")
public func matrix_transpose(_ x: ${mattype}) -> ${transtype} {
  return x.transpose
}

@available(swift, deprecated: 4, renamed: "==")
public func matrix_equal(_ lhs: ${mattype}, _ rhs: ${mattype}) -> Bool {
  return lhs == rhs
}

%  end # for cols in [2,3,4]
% end # for rows in [2,3,4]
%end # for type in floating_types
